/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package biofilter.records;

import biofilter.util.MappedPosition;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * An OperonRecord contains a grouping of ScoredRegionRecord that corespond to genes, start site and other qualities of a transcription unit.
 * IMPORTANT first, add all of the genes and gaps that the operon contains before attempting to fix the transcription start and stop sites.
 *
 * @author Daniel Quest
 * @date   April 15th 2010
 */
public class OperonRecord implements Record, MappedPosition, Comparable {
    private LinkedList<ScoredRegionRecord> features = null;
    private String seqid;
    private String source;
    private String type;
    private int start;  //start and end begin with the max and min values from the features in the LinkedList.  They can be adjusted later
    private int end;
    private char strand;
    public double score;
    private String phase;
    private String attributes;

    private String record = null;

    public OperonRecord(){
        init();
    }
    
    public OperonRecord(OperonRecord list1, OperonRecord list2){
        init();
        this.features = list1.features;
        this.add(list2);
    }

    private void init(){
        features = new LinkedList<ScoredRegionRecord>();
        this.start = Integer.MAX_VALUE;
        this.end = Integer.MIN_VALUE;
        this.strand = '.';
        this.type = "operon";

    }

    /**
     * size()
     * @return the number of features in the operon.
     */
    public int size(){
        return this.features.size();
    }

    public ScoredRegionRecord getPos(int i){
        if(this.features == null) return null;
        return (ScoredRegionRecord) this.features.get(i);
    }

    public void add(OperonRecord a){
        Iterator i = a.features.iterator();
        while(i.hasNext()){
            ScoredRegionRecord next = (ScoredRegionRecord) i.next();
            this.add(next);
        }

    }

    public void add(ScoredRegionRecord srr){
        boolean flag = false;  //if srr is already in the operon list
        Iterator i = this.features.iterator();
        while(i.hasNext()){
            ScoredRegionRecord next = (ScoredRegionRecord) i.next();
            if (next.compareTo(srr) == 0){
                flag = true;
            }
        }
        if(flag == false){
            this.features.add(srr);
            this.seqid =srr.getSeqid();
            this.source = "ORNLOperonFinder";
            if(srr.isType(this.typeGeneToken)){
                this.strand = srr.getStrand();
            }
            this.phase = ".";
            if(srr.isType(this.typeGeneToken)){
                if(this.start > srr.getStart()) this.start = srr.getStart();
                if(this.end < srr.getEnd()) this.end = srr.getEnd();
            }
            //sort the features
            //Collections.sort(this.features,Collections.reverseOrder());
            Collections.sort(this.features); //~~ or the other order if we want
        }
        return;
    }

    public void sort(){
        Collections.sort(this.features); 
    }

    public String getSeqid(){ return seqid;}
    public String getSource(){return source;}
    public String getType(){ return type;}
    public int getStart(){ return start;}
    public int getEnd(){ return end;}
    public double getScore(){ return score;}
    public char getStrand(){ return strand;}
    public String getPhase(){return phase;}
    public String getAttributes(){
        if (this.attributes == null){
            formatAttributes();
        }
        return attributes;
    }

    private String shortGene(GFFRecord gff){
        return gff.getStart() + " " + gff.getEnd() + " " + gff.getStrand();
    }
    
    private String typeGeneToken = "CDS";
    public String getTypeGeneToken() { return typeGeneToken; }
    public void setTypeGeneToken(String typeGeneToken) {  this.typeGeneToken = typeGeneToken;}
    private String formatAttributes(){
        String ats = "";
        Iterator i = this.features.iterator();
        while(i.hasNext()){
            ScoredRegionRecord next = (ScoredRegionRecord) i.next();
            if(next.isType(this.typeGeneToken)){  //if it is a gene.
                ats += shortGene(next.getGFF());
                ats += "; ";
            }
        }
        return ats;
    }

    private String typeGapToken = "GAP";
    public String getTypeGapToken() { return typeGapToken; }
    public void setTypeGapToken(String token) {  this.typeGapToken = token;}
    /**
     * assuming genome is from left to right...
     * @return leftmost gap from the operon
     * null if gap not found
     */
    public ScoredRegionRecord maxGap(){
        ScoredRegionRecord s = this.features.getLast();
        if(s.isType(this.typeGapToken)){
            return s;
        }else {
            return null;
        }
        //for (int i=this.features.size()-1; i>=0; i--){
        //    ScoredRegionRecord s = this.features.get(i);
        //    if(s.isType(typeGapToken)==true) return s;
        //}
        //return null;
    }

    /**
     * assuming genome is from left to right...
     * @return rightmost gap from the operon
     * null if gap not found
     */
    public ScoredRegionRecord minGap(){
        ScoredRegionRecord s = this.features.getFirst();
        if(s.isType(this.typeGapToken)){
            return s;
        }else {
            return null;
        }
        //for (int i=0; i<this.features.size(); i++){
        //    ScoredRegionRecord s = this.features.get(i);
        //    if(s.isType(typeGapToken)==true) return s;
        //}
        //return null;
    }

    private String formatRecord(){
        if(seqid == null ){  //more robust check needed here!
            return null;
        }
        String s = "";
        s+= seqid;
        s+= "\t" + source;
        s+= "\t" + type;
        s+= "\t" + start;
        s+= "\t" + end;
        s+= "\t" + this.getScore();
        s+= "\t" + strand;
        s+= "\t" + phase;
        s+= "\t" + formatAttributes();
        //s+= "\n";  //this is to be consistent with the rest of the architecture
        this.record = s;
        return record;
    }

    public String formatDebug(){
        String s = "";
        s += "#################################################################\n";
        s += "                         NEXT OPERON                             \n";
        s += "#################################################################\n";
        s += this.formatRecord();
        s += "\n";
        Iterator i = this.features.iterator();
        while(i.hasNext()){
            ScoredRegionRecord srr = (ScoredRegionRecord) i.next();
            s += srr.formatDebug();
        }
        return s;
    }

    public String get() {
        return formatRecord();
    }

    /**
     * This method is not intended to be used yet, the code to parse an operon
     * record into the required data structure does not exist.
     * Also, unless we code it in XML it is probably harder than rebuilding the operons anyway...
     * @param r
     * @throws IllegalArgumentException
     */
    public void set(String r) throws IllegalArgumentException {
        this.record = r;
    }

    public int length() {
        if (start < end) return end - start +1;
        else return start - end +1;
    }

    public double getWeight() {
        return this.score;
    }

    public String getQueryID() {
        return "."; //undefined... gff is for features of a sequence only 1 sequence exists!
    }

    public String getSubjectID() {
        return this.source;
    }

    public int compareTo(Object operon) {
        OperonRecord o = (OperonRecord) operon;
        if(this.end == o.end && this.start == o.start && this.strand == o.strand ) return 0;
        if(this.start > o.start || this.end > o.end ) return 1;
        return -1;
    }
}
